/*
 * Copyright 1999-2101 Alibaba Group.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.fastjson.serializer;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicLongArray;
import java.util.concurrent.atomic.AtomicReference;

/**
 * circular references detect
 * 
 * @author wenshao<szujobs@hotmail.com>
 * 
 */
public final class JSONSerializerMap {
	public static final int DEFAULT_TABLE_SIZE = 1024;

	private final Entry[] buckets;
	private final int indexMask;
	
	private final static JSONSerializerMap globalInstance = new JSONSerializerMap();
	
	public final static JSONSerializerMap getGlobalInstance() {
		return globalInstance;
	}

	public JSONSerializerMap() {
		this(DEFAULT_TABLE_SIZE);
	}

	public JSONSerializerMap(int tableSize) {
		this.indexMask = tableSize - 1;
		this.buckets = new Entry[tableSize];
		
		put(Boolean.class, BooleanSerializer.instance);
		put(Byte.class, ByteSerializer.instance);
		put(Short.class, ShortSerializer.instance);
		put(Integer.class, IntegerSerializer.instance);
		put(Long.class, LongSerializer.instance);
		put(Float.class, FloatSerializer.instance);
		put(Double.class, DoubleSerializer.instance);
		put(BigDecimal.class, BigDecimalSerializer.instance);
		put(BigInteger.class, BigIntegerSerializer.instance);
		put(String.class, StringSerializer.instance);
		put(byte[].class, ByteArraySerializer.instance);
		put(short[].class, ShortArraySerializer.instance);
		put(int[].class, IntArraySerializer.instance);
		put(long[].class, LongArraySerializer.instance);
		put(float[].class, FloatArraySerializer.instance);
		put(double[].class, DoubleArraySerializer.instance);
		put(boolean[].class, BooleanArraySerializer.instance);
		put(Integer[].class, IntegerArraySerializer.instance);
		put(String[].class, StringArraySerializer.instance);
		put(Object[].class, ObjectArraySerializer.instance);
		put(Class.class, ClassSerializer.instance);
		put(AtomicBoolean.class, AtomicBooleanSerializer.instance);
		put(AtomicInteger.class, AtomicIntegerSerializer.instance);
		put(AtomicLong.class, AtomicLongSerializer.instance);
		put(AtomicReference.class, AtomicReferenceSerializer.instance);
		put(AtomicIntegerArray.class, AtomicIntegerArraySerializer.instance);
		put(AtomicLongArray.class, AtomicLongArraySerializer.instance);
	}

	public final ObjectSerializer get(Class<?> clazz) {
		final int hash = System.identityHashCode(clazz);
		final int bucket = hash & indexMask;

		for (Entry entry = buckets[bucket]; entry != null; entry = entry.next) {
			if (clazz == entry.clazz) {
				return entry.serializer;
			}
		}

		return null;
	}

	public final boolean put(Class<?> clazz, ObjectSerializer serializer) {
		final int hash = System.identityHashCode(clazz);
		final int bucket = hash & indexMask;

		for (Entry entry = buckets[bucket]; entry != null; entry = entry.next) {
			if (clazz == entry.clazz) {
				return true;
			}
		}

		Entry entry = new Entry(clazz, serializer, hash, buckets[bucket]);
		buckets[bucket] = entry; // 并发是处理时会可能导致缓存丢失，但不影响正确性

		return false;
	}

	public int size() {
		int size = 0;
		for (int i = 0; i < buckets.length; ++i) {
			for (Entry entry = buckets[i]; entry != null; entry = entry.next) {
				size++;
			}
		}
		return size;
	}

	protected static final class Entry {
		public final int hashCode;
		public final Class<?> clazz;
		public final ObjectSerializer serializer;

		public final Entry next;

		public Entry(Class<?> clazz, ObjectSerializer serializer, int hash, Entry next) {
			this.clazz = clazz;
			this.serializer = serializer;
			this.next = next;
			this.hashCode = hash;
		}
	}
	
}
